package com.sw.bbs.user.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.sw.bbs.common.common.ErrorCode;
import com.sw.bbs.common.common.ServiceException;
import com.sw.bbs.common.constant.BBSConstant;
import com.sw.bbs.common.util.JacksonUtil;
import com.sw.bbs.common.util.UUIDUtil;
import com.sw.bbs.user.dao.UserMapper;
import com.sw.bbs.user.pojo.User;
import com.sw.bbs.user.service.UserService;
import com.sw.bbs.user.vo.LoginRequest;
import com.sw.bbs.user.vo.RegisterRequest;
import com.sw.bbs.user.vo.SendCodeRequest;
import com.yunpian.sdk.YunpianClient;
import com.yunpian.sdk.model.Result;
import com.yunpian.sdk.model.SmsSingleSend;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.sw.bbs.common.constant.BBSConstant.ACCESSTOKEN_CACHE_KEY;
import static com.sw.bbs.common.constant.BBSConstant.CAPTCHATIMEOUT;
import static com.sw.bbs.common.constant.BBSConstant.USERTIMEOUT;

@Service
public class UserServiceImpl implements UserService {

    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);


    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 登陆
     *
     * @param loginRequest
     * @return token
     */
    @Override
    public Map loginBy(LoginRequest loginRequest) throws ServiceException {

        // 转换密码
        String md5Password = DigestUtils.md5DigestAsHex(loginRequest.getPassword().getBytes());
        loginRequest.setPassword(md5Password);

        User user = userMapper.selectByLoginRequest(loginRequest);
        if (user != null) {
            return updateSaveAndGetTokenMap(user);
        } else {
            logger.error("login fail,query user error");
            throw new ServiceException(ErrorCode.REQUEST_PARAMETER_ERROR,"login fail,query user error");
        }
    }

    /**
     * 注册
     *
     * @param registerRequest
     * @return
     * @throws ServiceException
     */
    @Override
    @Transactional(rollbackFor = ServiceException.class)
    public Map registerBy(RegisterRequest registerRequest) throws ServiceException {


        //用户名是否存在
        if (userMapper.selectByUsername(registerRequest.getUsername()) != null) {

            logger.info("username exist");
            throw new ServiceException(ErrorCode.REQUEST_PARAMETER_ERROR, "username exist");
        }
        //手机是否存在
        if (userMapper.selectByUsername(registerRequest.getPhone()) != null) {

            logger.info("phone exist");
            throw new ServiceException(ErrorCode.REQUEST_PARAMETER_ERROR, "phone exist");
        }


        // 插入数据库
        User user = new User();
        String md5Password = DigestUtils.md5DigestAsHex(registerRequest.getPassword().getBytes());
        registerRequest.setPassword(md5Password);
        BeanUtils.copyProperties(registerRequest, user);

        int result = userMapper.insertSelective(user);
        if (result > 0) {
            try {
                return updateSaveAndGetTokenMap(user);
            }catch (ServiceException e) {
                throw new ServiceException(ErrorCode.INTERNAL_SERVER_ERROR, "insert redis failed");

            }
        } else {
            logger.info("insert user failed");
            throw new ServiceException(ErrorCode.INTERNAL_SERVER_ERROR, "insert user failed");
        }


    }

    /**
     * request获取用户登录信息
     *
     * @param request
     * @return
     */
    @Override
    public User getUserByRequest(HttpServletRequest request) throws ServiceException {
        String accessToken = request.getHeader(BBSConstant.ACCESSTOKEN);
        try {
            if (accessToken == null) {
                throw new ServiceException(ErrorCode.REQUEST_PARAMETER_ERROR, "request parameter error");
            }
            User user = getUserByToken(accessToken);
            if (user == null) {
                logger.error(">>>>>>get user info error|accessToken:{}", accessToken);
                throw new ServiceException(ErrorCode.INTERNAL_SERVER_ERROR, "get user info error");
            }
            return user;

        } catch (ServiceException e) {
            logger.error(">>>>>>get user info error: {},{}", e.getMessage(), accessToken);
            throw new ServiceException(ErrorCode.INTERNAL_SERVER_ERROR, "get user info error");
        }
    }

    @Override
    public User getUserByToken(String token) throws ServiceException {
        try {
            String key = String.format(ACCESSTOKEN_CACHE_KEY, token);
            String userInfo = redisTemplate.opsForValue().get(key);
            User user = JacksonUtil.readObjectFromJSON(userInfo, new TypeReference<User>(){});
            return user;
        }catch (Exception e) {
            throw new ServiceException(ErrorCode.BUSINESS_SERVER_ERROR,"token error");
        }

    }

    /**
     * 保存验证码在redis:一分钟
     * @throws ServiceException
     */
    @Override
    public Integer sendCode(SendCodeRequest request) throws ServiceException {

        String captcha = UUIDUtil.getUUID();

        //初始化clnt,使用单例方式
        YunpianClient clnt = new YunpianClient("apikey").init();

        //发送短信API
        Map<String, String> param = clnt.newParam(2);
        param.put(YunpianClient.MOBILE, request.getPhone());
        param.put(YunpianClient.TEXT, String.format("【FUNFUCK】您的验证码是%s",captcha));
        Result<SmsSingleSend> r = clnt.sms().single_send(param);
        //获取返回结果，返回码:r.getCode(),返回码描述:r.getMsg(),API结果:r.getData(),其他说明:r.getDetail(),调用异常:r.getThrowable()

        //账户:clnt.user().* 签名:clnt.sign().* 模版:clnt.tpl().* 短信:clnt.sms().* 语音:clnt.voice().* 流量:clnt.flow().* 隐私通话:clnt.call().*

        //释放clnt
        clnt.close();

        // key-value
        String key = String.format(ACCESSTOKEN_CACHE_KEY, captcha);
        try {
            redisTemplate.opsForValue().set(key, request.getPhone(), CAPTCHATIMEOUT, TimeUnit.SECONDS);
        } catch (ServiceException e) {
            logger.error("redis timeout fail");
            throw new ServiceException(ErrorCode.BUSINESS_SERVER_ERROR, "redis timeout fail");
        }
        return 1;
    }

    /**
     * 更新或者插入token去redis
     * @param user
     * @return
     * @throws ServiceException
     */
    public Map updateSaveAndGetTokenMap(User user) throws ServiceException{

        //生成新的token 1.存到redis(30天) 2.发送给用户

        String token = UUIDUtil.getUUID();
        String key = String.format(ACCESSTOKEN_CACHE_KEY, token);

        try {
            String userInfo =  JacksonUtil.writeObjectToJSON(user);
            redisTemplate.opsForValue().set(key,userInfo, USERTIMEOUT, TimeUnit.SECONDS);


        }catch (ServiceException e) {
            logger.error("redis server fail");
            throw new ServiceException(ErrorCode.BUSINESS_SERVER_ERROR,"redis server fail");
        }
        Map<String, String> map = new HashMap<String, String>();
        map.put(BBSConstant.ACCESSTOKEN, token);
        return map;
    }


}